package org.zw.android.framework.http;

import java.util.ArrayList;
import java.util.List;

import org.zw.android.framework.async.AsyncTaskExecutor;
import org.zw.android.framework.async.AsyncTaskHandler;
import org.zw.android.framework.async.AsyncTaskRunnable;
import org.zw.android.framework.http.ResponseBean.Status;
import org.zw.android.framework.log.Debug;
import org.zw.android.framework.util.StringUtils;

/**
 * Http Request Executor
 * 
 * @author zhouwei
 *
 */
public final class HttpFactory {
	
	private enum HttpMethod {
		GET , POST , PUT , DELETE
	}
	
	static final String TAG = "HttpService" ;
	
	// Global filters
	private static final List<FilterResponse> mFilters = new ArrayList<FilterResponse>();
	
	// HTTP Executor (Apache HTTP)
	private static HttpExecutor 	mExecutor ;
	
	static {
		mExecutor = new HttpExecutorApache() ;
	}
	
	public static void setHttpExecutor(HttpExecutor executor){
		mExecutor	= executor ;
	}
	
	/**
	 * clear Global filters 
	 */
	public static void clearFilters(){
		mFilters.clear() ;
	}
	
	/**
	 * Add Global Filter ; for ex : ErrorFilter , NormalFilter
	 * 
	 * @param filter
	 */
	public static void addFilter(FilterResponse filter){
		
		if(filter == null) return ;
		
		if(!mFilters.contains(filter)){
			mFilters.add(filter);
		}
	}
	
	public static void downloadFile(String url, final DownloadCallback callback,final HttpUIHandlerDownload handler){
		
		// If the request is null
		if (StringUtils.isEmpty(url))
			return;
		
		if(callback == null){
			Debug.e(TAG, "zhouwei >> Download file callback is null");
			return ;
		}
		
		final HttpRequest req = new HttpRequest(url);
		
		AsyncTaskExecutor.execute(new AsyncTaskRunnable(handler) {

			@Override
			public Object onProcessing() {
				
				try{
					
					ResponseBean input = mExecutor.downloadFile(req);
					
					if(input != null){
						
						Object res = callback.download(req,input,handler);
						
						if(handler != null){
							
							if(res != null){
								Debug.d(TAG, "zhouwei >> file download finish");
								handler.sendDownloadFinish(res) ;
							} else {
								handler.sendErrorMessage("Callback return is null");
							}
							
							return res ;
						}
						
						Debug.w(TAG, "zhouwei >> file download failed");
						
					} else {
						// send Exception to UI
						notifyException(new NullPointerException(req.getUrl() + " response is null"));
					}
					
				} catch(Exception e){
					e.printStackTrace() ;
					// send Exception to UI
					if(handler != null){
						notifyException(e);
					} else {
						// when exception
						ResponseBean bean = new ResponseBean() ;
						bean.setStatus(Status.FAILED);
						bean.setResponse(e.toString());
						doExceptionFilter(req,bean,handler);
					}
				}
				
				return null;
			}
			
		});
	}
	
	// send notify message with Object
	private static void sendMessage(HttpUIHandler handler, int what,Object value) {
		if (handler != null) {
			handler.sendMessage(what, value);
		}
	}

	// send notify message
	private static void sendMessage(HttpUIHandler handler, int what) {
		if (handler != null) {
			handler.sendMessage(what);
		}
	}
	
	/**
	 * async get
	 * @param request
	 * @param parser
	 * @param handler
	 */
	public static void doGet(final HttpRequest request,final HttpWrapper parser,HttpUIHandler handler){
		doGet(request, parser, handler,false) ;
	}
	
	/**
	 * sync get
	 * @param request
	 * @param parser
	 * @param handler
	 * @param async
	 */
	public static void doGet(final HttpRequest request,final HttpWrapper parser,HttpUIHandler handler,boolean async){
		doHttp(HttpMethod.GET, request, parser, handler,async) ;
	}
	
	/**
	 * do synchronization get request
	 * @param request
	 * @return
	 * @throws Exception
	 */
	public static ResponseBean doGet(HttpRequest request) throws Exception {
		return doSyncProcessing(HttpMethod.GET,request);
	}
	
	/**
	 * async post
	 * @param request
	 * @param parser
	 * @param handler
	 */
	public static void doPost(final HttpRequest request,final HttpWrapper parser,HttpUIHandler handler){
		doPost(request, parser, handler,false) ;
	}
	
	/**
	 * sync post
	 * @param request
	 * @param parser
	 * @param handler
	 * @param async
	 */
	public static void doPost(final HttpRequest request,final HttpWrapper parser,HttpUIHandler handler,boolean async){
		doHttp(HttpMethod.POST, request, parser, handler,async) ;
	}
	
	/**
	 * do synchronization post request
	 * @param request
	 * @return
	 * @throws Exception
	 */
	public static ResponseBean doPost(HttpRequest request) throws Exception {
		return doSyncProcessing(HttpMethod.POST,request);
	}
	
	/**
	 * async put
	 * @param request
	 * @param parser
	 * @param handler
	 */
	public static void doPut(final HttpRequest request,final HttpWrapper parser,HttpUIHandler handler){
		doPut(request, parser, handler,false) ;
	}
	
	/**
	 * sync put
	 * @param request
	 * @param parser
	 * @param handler
	 * @param async
	 */
	public static void doPut(final HttpRequest request,final HttpWrapper parser,HttpUIHandler handler,boolean async){
		doHttp(HttpMethod.PUT, request, parser, handler,async) ;
	}
	
	/**
	 * do synchronization put request
	 * @param request
	 * @return
	 * @throws Exception
	 */
	public static ResponseBean doPut(HttpRequest request) throws Exception {
		return doSyncProcessing(HttpMethod.PUT,request);
	}
	
	/**
	 * async delete
	 * 
	 * @param request
	 * @param parser
	 * @param handler
	 */
	public static void doDelete(final HttpRequest request,final HttpWrapper parser,HttpUIHandler handler){
		doDelete(request, parser, handler,false) ;
	}
	
	/**
	 * sync delete
	 * @param request
	 * @param parser
	 * @param handler
	 * @param async
	 */
	public static void doDelete(final HttpRequest request,final HttpWrapper parser,HttpUIHandler handler,boolean async){
		doHttp(HttpMethod.DELETE, request, parser, handler,async) ;
	}
	
	/**
	 * do synchronization put request
	 * @param request
	 * @return
	 * @throws Exception
	 */
	public static ResponseBean doDelete(HttpRequest request) throws Exception {
		return doSyncProcessing(HttpMethod.DELETE,request);
	}
	
	// do http
	private static void doHttp(final HttpMethod method ,
			final HttpRequest request,
			final HttpWrapper parser,
			final HttpUIHandler handler,
			boolean async){
		
		// If the request is null
		if(request == null) return ;
		
		// attach Handler
		if(parser != null) parser.attachUiHandler(handler);
		
		// sync task
		if(async){
			
			if(parser != null && !parser.onStart()){
				sendMessage(handler,AsyncTaskHandler.MSG_STATUS_CANCEL);
				return ;
			}
			
			// start
			sendMessage(handler,AsyncTaskHandler.MSG_STATUS_START);
			
			// process
			Object obj = doProcessing(method,request,parser,handler);
			
			// send execute completed message
			if(obj != null){
				sendMessage(handler,AsyncTaskHandler.MSG_STATUS_FINISH,obj);
			}
			
			// handler task end message
			sendMessage(handler,AsyncTaskHandler.MSG_STATUS_COMPLETED);
		} 
		// async task
		else {
			AsyncTaskExecutor.execute(new AsyncTaskRunnable(handler) {
				
				@Override
				public boolean onStart() {
					return parser != null ? parser.onStart() : true ;
				}
				
				@Override
				public Object onProcessing() {
					return doProcessing(method,request,parser,handler);
				}
			}) ;
		}
	}
	
	// do synchronization request
	public static ResponseBean doSyncProcessing(HttpMethod method,HttpRequest request) throws Exception{
		
		if(method == null || request == null) return null ;
		
		if(method == HttpMethod.GET){
			return mExecutor.doGet(request);
		} else if(method == HttpMethod.POST){
			return mExecutor.doPost(request);
		} else if(method == HttpMethod.DELETE){
			return mExecutor.doDelete(request);
		} else {
			return mExecutor.doPut(request);
		}
	}
	
	// do execute
	private static Object doProcessing(HttpMethod method,HttpRequest request,HttpWrapper parser,HttpUIHandler handler){
		
		// Execute HTTP
		try {
			
			long enter = System.currentTimeMillis() ;
			
			ResponseBean bean = null ;
			
			if(method == HttpMethod.GET){
				bean = mExecutor.doGet(request);
			} else if(method == HttpMethod.POST){
				bean = mExecutor.doPost(request);
			} else if(method == HttpMethod.DELETE){
				bean = mExecutor.doDelete(request);
			} else if(method == HttpMethod.PUT){
				bean = mExecutor.doPut(request);
			}
			
			Debug.d(TAG, "zhouwei >> Server Response time = " + (System.currentTimeMillis() - enter));
			
			// filter normal
			if(request.isFilter() && !doNormalFilter(request,bean,handler)){
				
				Debug.w(TAG, "zhouwei >> filter process failed");
				
				// filter by spec wrapper
				if(parser != null){
					parser.onError(bean.getResponse());
				} else {
					if(handler != null){
						handler.sendNormalFilterFailed(bean != null ? bean.getResponse() : null);
					}
				}
				
				return null ;
			}
			
			String obj = bean != null ? bean.getResponse() : null ;
			
			// if parser filter is null
			if(parser == null){
				
				Debug.w(TAG, "zhouwei >> http request parser filter is null");
				
				if(handler != null){
					handler.sendParserFilterFailed("parser filter is null");
				}
				
				return obj ;
			}
			
			enter = System.currentTimeMillis() ;
			
			// parser Filter
			Object res = parser.wrapper(obj) ;
			
			Debug.d(TAG, "zhouwei >> FilterParser time = " + (System.currentTimeMillis() - enter));
			
			return res ;
		} catch(Exception e) {
			e.printStackTrace() ;
			
			// send Exception to UI
			if (handler != null) {
				handler.sendErrorMessage(e);
			} else {
				ResponseBean bean = new ResponseBean() ;
				bean.setStatus(Status.FAILED);
				bean.setResponse(e.toString());
				doExceptionFilter(request,bean,handler);
			}
			
			return null ;
		}
	}
	
	// Normal
	private static boolean doNormalFilter(HttpRequest request,ResponseBean bean,HttpUIHandler handler){
		
		final List<FilterResponse> temp = mFilters ;
		
		// filter normal
		for(FilterResponse f : temp){
			
			// if filter return true, the process stop and process next
			if(f instanceof FilterNormal){
				if(f.doFilter(request, bean,handler)){
					return true ;
				}
			}
		}
		
		return false ;
	}
	
	// Exception
	private static void doExceptionFilter(HttpRequest request,ResponseBean bean,HttpUIHandler handler){
		
		final List<FilterResponse> temp = mFilters ;
		
		// filter exception
		for(FilterResponse f : temp){
			
			// if filter return true, the process stop
			if(f instanceof FilterException){
				if(f.doFilter(request, bean,handler)) return;
			}
		}
	}
}
